package com.karson.service.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.cron.TaskExecutor;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.karson.common.config.redis.RedisConfig;
import com.karson.common.constants.BusinessConstants;
import com.karson.common.constants.Constants;
import com.karson.common.enums.EntityType;
import com.karson.common.exception.AsmrException;
import com.karson.common.model.*;
import com.karson.common.request.AlbumSearchRequest;
import com.karson.common.request.PageParamRequest;
import com.karson.common.response.AlbumResponse;
import com.karson.common.response.AuthorResponse;
import com.karson.common.util.RedisUtil;
import com.karson.common.vo.CircleVo;
import com.karson.common.vo.PageVo;
import com.karson.common.vo.TagVo;
import com.karson.service.dao.AlbumInfoMapper;
import com.karson.service.dao.BehaviorWeightMapper;
import com.karson.service.dao.UserActionMapper;
import com.karson.service.service.*;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import javax.annotation.Syntax;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author ZKS
 */
@Slf4j
@Service
public class RecommendServiceImpl implements RecommendService {
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private AlbumInfoMapper albumInfoMapper;
    @Autowired
    private AlbumInfoService albumInfoService;
    @Autowired
    private UserActionMapper userActionMapper;
    @Autowired
    private BehaviorWeightMapper behaviorWeightMapper;
    @Autowired
    private HotRecommendService hotRecommendService;
    @Autowired
    private AlbumTagsService albumTagsService;
    @Autowired
    private AlbumAuthorService albumAuthorService;
    @Qualifier("asyncExecutor")
    @Autowired
    private ThreadPoolTaskExecutor threadPoolExecutor;

    private final ConcurrentHashMap<Integer, ReentrantLock> userLocks = new ConcurrentHashMap<>();

    @Override
    public PageVo<AlbumResponse> getInterestList(PageParamRequest pageParamRequest) {
        int uid = StpUtil.getLoginIdAsInt();
        String key = RedisConfig.INTEREST_RECOMMEND + uid;
        String expireKey = RedisConfig.INTEREST_RECOMMEND_EXP + uid;

        // 1. 先读取缓存列表
        PageVo<AlbumResponse> pageVo = getAlbumResponsePageVo(pageParamRequest, key);
        // 1.1 被物理删除了，重建缓存并返回数据
        if (pageVo == null){
            pageVo = new PageVo<AlbumResponse>();
            pageVo.setCurrent(pageParamRequest.getCurrent());
            pageVo.setSize(pageParamRequest.getSize());
            Map<Object, Double> candidateAlbums = getCandidateAlbums(uid);
            if (candidateAlbums != null && candidateAlbums.isEmpty()){
                // 没有推荐数据，用户没有任何行为的时候不予推荐
                return new PageVo<>();
            }
            List<Integer> albumIdList = null;
            if (candidateAlbums != null) {
                albumIdList = candidateAlbums.keySet().stream()
                        .map(id -> (Integer) id)
                        .toList();
            }
            // 当前页
            int current = pageParamRequest.getCurrent();
            // 每页条数
            int size = pageParamRequest.getSize();
            int fromIndex = Math.min((current - 1) * size, albumIdList.size());
            int toIndex = Math.min(current * size, albumIdList.size());
            List<Integer> pageAlbumIds = albumIdList.subList(fromIndex, toIndex);
            List<AlbumResponse> albumByIds = albumInfoMapper.getAlbumByIds(pageAlbumIds);
            pageVo.setTotal((long) albumIdList.size());
            int totalPages = (int) Math.ceil((double) albumIdList.size() / size);
            pageVo.setPages(totalPages);
            pageVo.setRecords(albumByIds);
            return pageVo;
        }

        // 2. 判断是否逻辑过期
        String expireAtStr = redisUtil.get(expireKey);
        long now = System.currentTimeMillis();
        if (StrUtil.isNotBlank(expireAtStr)) {
            long expireAt = Long.parseLong(expireAtStr);
            if (expireAt < now) {
                // 3. 获取该用户的独立锁 如果单用户正常操作不可能有并发量，加锁非必须。
                ReentrantLock lock = userLocks.computeIfAbsent(uid, k -> new ReentrantLock());
                if (lock.tryLock()) {
                    try {
                        CompletableFuture.runAsync(() -> getCandidateAlbums(uid), threadPoolExecutor);
                    } finally {
                        lock.unlock();
                    }
                }
            }
        }
        // 4. 逻辑过期，先返回旧缓存，后台重建缓存
        return pageVo;
    }
    @Override
    public Map<Object, Double> getCandidateAlbums(Integer uid) {
        String tagWeightKey = RedisConfig.USER_WEIGHT_KEY + uid + ":" + EntityType.TAG.getCode();
        String authorWeightKey = RedisConfig.USER_WEIGHT_KEY + uid + ":" + EntityType.AUTHOR.getCode();
        String circleWeightKey = RedisConfig.USER_WEIGHT_KEY + uid + ":" + EntityType.CIRCLE.getCode();

        // 1. 取出用户兴趣ID 和 权重 (标签可以多一点）
        Map<Object, Double> tagWeight = redisUtil.getLatest(tagWeightKey, 15);
        Map<Object, Double> authorWeight = redisUtil.getLatest(authorWeightKey, 10);
        Map<Object, Double> circleWeight = redisUtil.getLatest(circleWeightKey, 10);
        // 2. 取出用户的兴趣Id 和 权重总和
        List<Integer> tagIds = extractIds(tagWeight);
        List<Integer> authorIds = extractIds(authorWeight);
        List<Integer> circleIds = extractIds(circleWeight);
        double tagTotal = calcTotalWeight(tagWeight);
        double authorTotal = calcTotalWeight(authorWeight);
        double circleTotal = calcTotalWeight(circleWeight);
        // 2.1 计算不同类型标签的兴趣分布比例
        double total = tagTotal + authorTotal + circleTotal;
        double tagRatio = total > 0 ? tagTotal / total : 0;
        double authorRatio = total > 0 ? authorTotal / total : 0;
        double circleRatio = total > 0 ? circleTotal / total : 0;
        // 3. 按照普通标签，作者标签，社团标签分别召回候选集
        // 总召回数量
        int totalRecallSize = 200;
        int tagRecallSize = (int) Math.round(totalRecallSize * tagRatio);
        int authorRecallSize = (int) Math.round(totalRecallSize * authorRatio);
        int circleRecallSize = (int) Math.round(totalRecallSize * circleRatio);
        // 4. 执行子召回
        // TODO 可以开启多线程异步召回合并 召回内容（分为按标签时间召回，或按标签随机召回交由用户选择）
        List<Integer> tagCandidates = recallByTag(tagWeightKey,tagIds, BusinessConstants.BUSINESS_TAG,tagRecallSize,tagTotal);
        List<Integer> authorCandidates = recallByTag(authorWeightKey,authorIds,BusinessConstants.BUSINESS_AUTHOR, authorRecallSize,authorTotal);
        List<Integer> circleCandidates = recallByTag(circleWeightKey,circleIds,BusinessConstants.BUSINESS_CIRCLE, circleRecallSize,circleTotal);
        Map<Integer, Double> scoreMap = new HashMap<>(totalRecallSize);

        tagCandidates.forEach(albumId ->
                scoreMap.merge(albumId, tagRatio, Double::sum));

        authorCandidates.forEach(albumId ->
                scoreMap.merge(albumId, authorRatio, Double::sum));
        circleCandidates.forEach(albumId ->
                scoreMap.merge(albumId, circleRatio, Double::sum));

        // 最终按得分从高到低排序
        Map<Object, Double> albumIdWithScore = scoreMap.entrySet().stream()
                .sorted((a, b) -> Double.compare(b.getValue(), a.getValue()))
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        Map.Entry::getValue,
                        (oldV, newV) -> oldV,
                        LinkedHashMap::new
                ));
        String interestKey = RedisConfig.INTEREST_RECOMMEND + uid;
        redisUtil.zAddBatch(interestKey,albumIdWithScore);
        // 物理过期，避免长期不上线用户占用缓存空间 ， 仅暂存2天
        redisUtil.expire(interestKey,RedisConfig.CACHE_TTL_SECONDS * 48);
        // TTL 1小时 ， 一个小时过期重新拉取推荐列表
        String expireKey = RedisConfig.INTEREST_RECOMMEND_EXP + uid;
        // 设置逻辑过期时间戳，额外的逻辑过期时间用于缓存重建更新用户兴趣列表
        long logicExpireTime = System.currentTimeMillis() + RedisConfig.LOGICAL_EXPIRE_MILLIS;
        redisUtil.set(expireKey, String.valueOf(logicExpireTime));
        return albumIdWithScore;
    }
    private List<Integer> recallByTag(String key,List<Integer> ids,String type, int recallSize,double totalWeight) {
        List<Integer> allAlbumList = new ArrayList<>();
        for (Integer id : ids) {
            Double weight = redisUtil.zScore(key, id);
            if (weight == null) {
                continue;
            }
            // 确定该兴趣项在总召回数中的比例
            double ratio = totalWeight > 0 ? weight / totalWeight : 0;
            long recallCount = Math.round(ratio * recallSize);

            if (Objects.equals(type, BusinessConstants.BUSINESS_TAG)) {
                LambdaQueryWrapper<ArAlbumTag> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(ArAlbumTag::getTagId, id)
                        .last("LIMIT " + recallCount);
                List<ArAlbumTag> list = albumTagsService.list(queryWrapper);
                List<Integer> albumList = list.stream()
                        .map(ArAlbumTag::getAlbumId)
                        .toList();

                allAlbumList.addAll(albumList);
            }
            if (Objects.equals(type, BusinessConstants.BUSINESS_AUTHOR)) {
                LambdaQueryWrapper<AlbumAuthor> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(AlbumAuthor::getAuthorId, id)
                        .last("LIMIT " + recallCount);
                List<AlbumAuthor> list = albumAuthorService.list(queryWrapper);
                List<Integer> albumList = list.stream()
                        .map(AlbumAuthor::getAlbumId)
                        .toList();
                allAlbumList.addAll(albumList);
            }
            if (Objects.equals(type, BusinessConstants.BUSINESS_CIRCLE)) {
                LambdaQueryWrapper<AlbumInfo> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.select(AlbumInfo::getId).eq(AlbumInfo::getCircleId,id).last("LIMIT " + recallCount);
                List<AlbumInfo> albumInfoList = albumInfoService.list(queryWrapper);
                List<Integer> albumList = albumInfoList.stream().map(AlbumInfo::getId).toList();
                allAlbumList.addAll(albumList);
            }
        }

        return allAlbumList;
    }
    private List<Integer> extractIds(Map<Object, Double> map) {
        return map.keySet().stream()
                .map(id -> Integer.valueOf(id.toString()))
                .collect(Collectors.toList());
    }

    private double calcTotalWeight(Map<Object, Double> map) {
        if (map == null || map.isEmpty()) {
            return 0.0;
        }
        return map.values().stream()
                .mapToDouble(Double::doubleValue)
                .sum();
    }

    /**
     * 当日热播
     * @return
     */
    @Override
    public Map<Object, Double> getDayHotRecommendTask() {
        // 聚合当天的操作记录
        List<ActionCounts> actionCounts = userActionMapper.getDayActions();
        if (actionCounts.isEmpty()) {
            return Collections.emptyMap();
        }

        List<Integer> albumIds = actionCounts.stream()
                .map(ActionCounts::getMessageId)
                .toList();

        Map<Integer, ActionCounts> actionCountsMap = actionCounts.stream()
                .collect(Collectors.toMap(ActionCounts::getMessageId, a -> a));

        // 一次性查出所有专辑信息（AlbumInfo）
        List<AlbumInfo> albumInfoList = albumInfoService.listByIds(albumIds);
        Map<Integer, AlbumInfo> albumMap = albumInfoList.stream()
                .collect(Collectors.toMap(AlbumInfo::getId, a -> a));

        // 行为权重表只查一次
        List<BehaviorWeight> weights = behaviorWeightMapper.selectList(
                new LambdaQueryWrapper<BehaviorWeight>().eq(BehaviorWeight::getIsDel, false)
        );
        Map<String, Double> weightMap = weights.stream()
                .collect(Collectors.toMap(BehaviorWeight::getBehavior, BehaviorWeight::getWeight));

        // 调用热度计算服务并计算所有分数
        Map<Object, Double> hotAlbums = new HashMap<>(albumIds.size());
        for (Integer albumId : albumIds) {
            ActionCounts actionCount = actionCountsMap.get(albumId);
            AlbumInfo albumInfo = albumMap.get(albumId);
            if (actionCount == null || albumInfo == null) {
                continue;
            }

            double hotScore = albumInfoService.computeHotScore(actionCount, weightMap);
            hotAlbums.put(albumId, hotScore);
        }

        // 只保留分数前 50 名
        return hotAlbums.entrySet()
                .stream()
                .sorted(Map.Entry.<Object, Double>comparingByValue(Comparator.reverseOrder()))
                .limit(50)
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        Map.Entry::getValue,
                        (a, b) -> a,
                        LinkedHashMap::new
                ));
    }

    /**
     * 定时任务触发
     * 保存当天结算的热播排行，用于数据兜底，以及数据分析
     */
    @Override
    public void dailySettlement() {
        Map<Object, Double> dailyContent = getDayHotRecommendTask();
        if (dailyContent == null) {
            // 当日没有用户产生行为
            return;
        }
        // 按 value（热度分数）倒序排序
        List<Map.Entry<Object, Double>> sortedList = dailyContent.entrySet()
                .stream()
                .sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue()))
                .toList();
        List<HotRecommend> hotList = new ArrayList<>();
        int rank = 1;

        for (Map.Entry<Object, Double> entry : sortedList) {
            HotRecommend hot = new HotRecommend();
            hot.setMessageId((Integer) entry.getKey());
            hot.setHotScore(BigDecimal.valueOf(entry.getValue()));
            hot.setRankIndex(rank++);
            hot.setRankDate(new Date());
            hotList.add(hot);
        }
        hotRecommendService.saveBatch(hotList);
    }

    @Override
    public PageVo<AlbumResponse> getHotRecommendList(String period, PageParamRequest pageParamRequest) {
        String key = RedisConfig.HOT_ALBUMS + period;
        int startIndex = (pageParamRequest.getCurrent() - 1) * pageParamRequest.getSize();
        PageVo<AlbumResponse> pageVo = new PageVo<>();
        pageVo.setCurrent(pageParamRequest.getCurrent());
        pageVo.setSize(pageParamRequest.getSize());
        if (redisUtil.exists(key)){
            long size = redisUtil.size(key);
            if (startIndex >= size){
                throw new AsmrException("分页参数异常");
            }
            Set<Object> page = redisUtil.getPage(key, pageParamRequest.getCurrent(), pageParamRequest.getSize());
            List<Integer> ids = page.stream()
                    .map(Object::toString)
                    .map(Integer::valueOf)
                    .toList();
            if (size <= 20){
                //拉取昨日的推荐暂时补充数据
                List<Integer> lastRecommend =  hotRecommendService.getLastRecommend();
                LinkedHashSet<Integer> mergedSet = new LinkedHashSet<>(ids);
                // 自动去重且保留插入顺序
                mergedSet.addAll(lastRecommend);
                ids = new ArrayList<>(mergedSet);
            }
            pageVo.setTotal(size);
            pageVo.setPages((int) ((size + pageParamRequest.getSize() - 1) / pageParamRequest.getSize()));
            List<AlbumResponse> albumInfoByIds = albumInfoService.getAlbumInfoByIds(ids);
            pageVo.setRecords(albumInfoByIds);
            return pageVo;
        }
        // 缓存未命中
        if (Objects.equals(period, Constants.TERM_DAY)){
            // 当日热播查询
            Map<Object, Double> dayHotRecommend = getDayHotRecommendTask();
            if (dayHotRecommend == null) {
                // 当日没有用户产生行为,获取昨日数据
              List<Integer> ids =  hotRecommendService.getLastRecommend();
                pageVo.setTotal((long) ids.size());
                pageVo.setPages((ids.size() + pageParamRequest.getSize() - 1) / pageParamRequest.getSize());
                List<AlbumResponse> albumInfoByIds = albumInfoService.getAlbumInfoByIds(ids);
                pageVo.setRecords(albumInfoByIds);
              return pageVo;
            }
            //每小时重新计算一下当前热播
            redisUtil.zAddBatch(key,dayHotRecommend);
            redisUtil.expire(key,RedisConfig.CACHE_TTL_SECONDS);
            List<Integer> ids = dayHotRecommend.entrySet().stream()
                    .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
                    .map(e -> Integer.parseInt(e.getKey().toString()))
                    .toList();
            pageVo.setTotal((long) ids.size());
            pageVo.setPages((ids.size() + pageParamRequest.getSize() - 1) / pageParamRequest.getSize());
            List<AlbumResponse> albumInfoByIds = albumInfoService.getAlbumInfoByIds(ids);
            pageVo.setRecords(albumInfoByIds);
            return pageVo;
        }
        return new PageVo<>();
    }

    @Override
    public void monthSettlement() {
        // 1. 查询上个月更新的专辑
        List<AlbumInfo> albumInfos = albumInfoMapper.monthSettlement();
        if (albumInfos == null || albumInfos.isEmpty()) {
            log.info("上个月没有更新的专辑");
            return;
        }
        // 2.1 如果上个月有更新则删除现有缓存重新拉取
        String key = RedisConfig.HOT_ALBUMS + Constants.TERM_MONTH;
        redisUtil.delete(key);
        // 2.2 计算热度值（或者已有的 hotScore）
        Map<Object, Double> hotAlbums = new HashMap<>(albumInfos.size());
        for (AlbumInfo album : albumInfos) {
            double score = album.getHotScore() != null ? album.getHotScore().doubleValue() : 0.0;
            hotAlbums.put(album.getId(), score);
        }
        // 3. 存入 Redis ZSet（按热度排序）
        redisUtil.zAddBatch(key, hotAlbums);
    }

    @Override
    public PageVo<AlbumResponse> getAllHotRank(PageParamRequest pageParamRequest) {
        String key = RedisConfig.HOT_RANK;
        return getAlbumResponsePageVo(pageParamRequest, key);
    }

    @Override
    public List<Integer> getInterestListMein(int uid) {
        // 分三类进行推荐 作者 ， 标签， 社团
        String tagWeightKey = RedisConfig.USER_WEIGHT_KEY + uid + ":" + EntityType.TAG.getCode();
        String authorWeightKey = RedisConfig.USER_WEIGHT_KEY + uid + ":" + EntityType.AUTHOR.getCode();
        String circleWeightKey = RedisConfig.USER_WEIGHT_KEY + uid + ":" + EntityType.CIRCLE.getCode();
        Map<Object, Double> tagWeight = redisUtil.getLatest(tagWeightKey, 10);
        Map<Object, Double> authorWeight = redisUtil.getLatest(authorWeightKey, 10);
        Map<Object, Double> circleWeight = redisUtil.getLatest(circleWeightKey, 10);
        double tagTotal = calcTotalWeight(tagWeight);
        double authorTotal = calcTotalWeight(authorWeight);
        double circleTotal = calcTotalWeight(circleWeight);
        double total = tagTotal + authorTotal + circleTotal;
        double tagRatio = total > 0 ? tagTotal / total : 0;
        double authorRatio = total > 0 ? authorTotal / total : 0;
        String[] tagArray = initProbabilityArray(tagWeight);
        String[] authorArray = initProbabilityArray(authorWeight);
        String[] circleArray = initProbabilityArray(circleWeight);
        // 拉取一次的总召回数
        int totalRecall = 10;
        // 按比例添加各维度数组
        int tagCount = (int) (totalRecall * tagRatio);
        int authorCount = (int) (totalRecall * authorRatio);
        int circleCount = totalRecall - tagCount - authorCount;

        List<Map<String, Object>> probabilityArray = new ArrayList<>(totalRecall);
        // 随机填充
        Random random = new Random();
        for (int i = 0; i < tagCount; i++) {
            Integer id = Integer.valueOf(tagArray[random.nextInt(tagArray.length)]);
            probabilityArray.add(Map.of("type",BusinessConstants.BUSINESS_TAG,"id",id));
        }
        for (int i = 0; i < authorCount; i++) {
            Integer id = Integer.valueOf(authorArray[random.nextInt(authorArray.length)]);
            probabilityArray.add(Map.of("type",BusinessConstants.BUSINESS_AUTHOR,"id",id));
        }
        for (int i = 0; i < circleCount; i++) {
            Integer id = Integer.valueOf(circleArray[random.nextInt(circleArray.length)]);
            probabilityArray.add(Map.of("type",BusinessConstants.BUSINESS_CIRCLE,"id",id));
        }
        // 从倒排索引中随机读取该标签下的作品
        String temp = RedisConfig.SYSTEM_STOCK;
        List<Object> list = redisUtil.executePipeline(redisConnection -> {
            for (Map<String, Object> labelMap : probabilityArray) {
                String key = temp + labelMap.get("type") + ":" + labelMap.get("id");
                redisConnection.sRandMember(key.getBytes());
            }
        });
        Set<Integer> finalVideoIds = list.stream()
                .filter(Objects::nonNull)
                .map(o -> Integer.parseInt(o.toString()))
                .collect(Collectors.toSet());
        String historyKey = RedisConfig.SYSTEM_HISTORY_TEMP + uid;
        List<Integer> filterIds = finalVideoIds.stream().filter(id -> {
            return !redisUtil.sIsMember(historyKey, id);
        }).toList();
        return new ArrayList<>(filterIds);
    }

    @Override
    public List<AlbumResponse> getInterestWithDistinct(List<Integer> ids) {
        //拉取下一批推荐列表时，将已经推荐过给用户的作品存到暂存库中
        int uid = StpUtil.getLoginIdAsInt();
        if (ids != null && !ids.isEmpty()) {
            //暂存历史记录
            String historyKey = RedisConfig.SYSTEM_HISTORY_TEMP + uid;
            redisUtil.executePipeline(redisConnection -> {
               for (Integer id : ids) {
                   redisConnection.setCommands().sAdd(historyKey.getBytes(),id.toString().getBytes());
               }
            });
            // 1 天内不推荐已推荐过内容
            redisUtil.expire(historyKey,RedisConfig.CACHE_TTL_SECONDS * 24);
        }
        List<Integer> interestListMein = getInterestListMein(uid);
        if (interestListMein.size() < 10){
            // 不足10 条 从热门中随机抽取补全
            String hotKey = RedisConfig.HOT_RANK;
            List<Integer> hotAlbumIds = redisUtil.randomMembers(hotKey, 10 - interestListMein.size());
            interestListMein.addAll(hotAlbumIds);
        }
        return albumInfoMapper.getAlbumByIds(interestListMein);
    }

    public String[] initProbabilityArray(Map<Object, Double> modelMap) {
        // key: 标签  value：概率
        Map<String, Integer> probabilityMap = new HashMap<>();
        final AtomicInteger n = new AtomicInteger(0);

        modelMap.forEach((k, v) -> {
            // 放大比例，避免小数精度丢失，例如保留两位小数
            double probability = (v + 1.0) * 10; // “+1.0” 防止太小或为0

            int p = (int) Math.round(probability);
            n.addAndGet(p);
            probabilityMap.put(k.toString(), p);
        });

        final String[] probabilityArray = new String[n.get()];
        final AtomicInteger index = new AtomicInteger(0);

        probabilityMap.forEach((labelId, p) -> {
            int i = index.get();
            int limit = i + p;
            while (i < limit && i < probabilityArray.length) {
                probabilityArray[i++] = labelId;
            }
            index.set(limit);
        });

        return probabilityArray;
    }
    private PageVo<AlbumResponse> getAlbumResponsePageVo(PageParamRequest pageParamRequest, String key) {
        int startIndex = (pageParamRequest.getCurrent() - 1) * pageParamRequest.getSize();
        PageVo<AlbumResponse> pageVo = new PageVo<>();
        pageVo.setCurrent(pageParamRequest.getCurrent());
        pageVo.setSize(pageParamRequest.getSize());
        if (redisUtil.exists(key)) {
            long size = redisUtil.size(key);
            if (startIndex >= size) {
                throw new AsmrException("分页参数异常");
            }
            Set<Object> page = redisUtil.getPage(key, pageParamRequest.getCurrent(), pageParamRequest.getSize());
            List<Integer> ids = page.stream()
                    .map(Object::toString)
                    .map(Integer::valueOf)
                    .toList();
            pageVo.setTotal(size);
            pageVo.setPages((int) ((size + pageParamRequest.getSize() - 1) / pageParamRequest.getSize()));
            List<AlbumResponse> albumInfoByIds = albumInfoService.getAlbumInfoByIds(ids);
            pageVo.setRecords(albumInfoByIds);
        }else {
            // 缓存压根没有，那么返回空对象
            return null;
        }
        return pageVo;
    }
}
